﻿using System;
using System.Collections;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.DirectoryServices.Protocols;
using System.Security.Principal;
using ActiveDirectoryUtilities.DirectoryServiceAccess;
using AccessActiveDirectory;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{
	[SqlFunction(FillRowMethodName = "FillUserObjectRow", DataAccess = DataAccessKind.Read
		, TableDefinition =
		//class general columns
		"objectGuid UNIQUEIDENTIFIER"
		+ ", distinguishedName NVARCHAR(MAX)"
		+ ", sAMAccountName NVARCHAR(MAX)"
		+ ", lastLogonTimestamp DATETIME"
		+ ", pwdLastSet DATETIME"
		+ ", userAccountControl INT"
		+ ", ADS_UF_SCRIPT BIT"
		+ ", ADS_UF_ACCOUNTDISABLE BIT"
		+ ", ADS_UF_HOMEDIR_REQUIRED BIT"
		+ ", ADS_UF_LOCKOUT BIT"
		+ ", ADS_UF_PASSWD_NOTREQD BIT"
		+ ", ADS_UF_PASSWD_CANT_CHANGE BIT"
		+ ", ADS_UF_ENCRYPTED_TEXT_PWD_ALLOWED BIT"
		+ ", ADS_UF_TEMP_DUPLICATE_ACCOUNT BIT"
		+ ", ADS_UF_NORMAL_ACCOUNT BIT"
		+ ", ADS_UF_INTERDOMAIN_TRUST_ACCOUNT BIT"
		+ ", ADS_UF_WORKSTATION_TRUST_ACCOUNT BIT"
		+ ", ADS_UF_SERVER_TRUST_ACCOUNT BIT"
		+ ", ADS_UF_DONT_EXPIRE_PASSWD BIT"
		+ ", ADS_UF_MNS_LOGON_ACCOUNT BIT"
		+ ", ADS_UF_SMARTCARD_REQUIRED BIT"
		+ ", ADS_UF_TRUSTED_FOR_DELEGATION BIT"
		+ ", ADS_UF_NOT_DELEGATED BIT"
		+ ", ADS_UF_USE_DES_KEY_ONLY BIT"
		+ ", ADS_UF_DONT_REQ_PREAUTH BIT"
		+ ", ADS_UF_PASSWORD_EXPIRED BIT"
		+ ", ADS_UF_TRUSTED_TO_AUTH_FOR_DELEGATION BIT"
		+ ", ADS_UF_PARTIAL_SECRETS_ACCOUNT BIT"

		//Category specific columns
		+ ", displayName NVARCHAR(MAX)"
		+ ", msExchHomeServerName NVARCHAR(MAX)"
		+ ", homeMDB NVARCHAR(MAX)"
		+ ", mDBOverQuotaLimit BIGINT"
		+ ", telephoneNumber NVARCHAR(MAX)"
		+ ", extensionAttribute7 NVARCHAR(MAX)"
		+ ", employeeId NVARCHAR(MAX)"
		+ ", msRTCSIP_PrimaryHomeServer NVARCHAR(MAX)"
		+ ", msRTCSIP_UserEnabled BIT"
		+ ", msRTCSIP_PrimaryUserAddress NVARCHAR(MAX)"
		)]
	public static IEnumerable fn_GetUserObjects(
		SqlString connectionPoint, SqlInt32 portNumber, SqlString baseDn, SqlString filter, SqlString searchScope)
	{
		EventLogTraceListener eltl = new EventLogTraceListener("Application");
		if (ConfiguredSettings.TraceLevel >= 4)
		{
			Trace.Listeners.Add(eltl);
		}

		WindowsImpersonationContext executionContext = null;
		try
		{
			WindowsIdentity callerId = SqlContext.WindowsIdentity;
			if (callerId != null)
			{
				executionContext = callerId.Impersonate();

				string[] attrs = { "objectGuid"
							,"distinguishedName"
							, "sAMAccountName"
							, "lastLogonTimestamp"
							, "pwdLastSet"
							, "userAccountControl"
							, "displayName"
							, "msExchHomeServerName"
							, "homeMDB"
							, "mDBOverQuotaLimit"
							, "telephoneNumber"
							, "extensionAttribute7"
							, "employeeId"
							, "msRTCSIP-PrimaryHomeServer"
							, "msRTCSIP-UserEnabled"
							, "msRTCSIP-PrimaryUserAddress"
						 };

				#region Check inputs
				if ((connectionPoint.IsNull) || (connectionPoint.CompareTo("") == 0))
				{
					throw new ApplicationException("ConnectionPoint parameter can not be null or blank.");
				}

				if (portNumber.IsNull)
				{
					throw new ApplicationException("PortNumber parameter can not be null.");
				}

				string internalBaseDn = null;
				if ((baseDn.IsNull) || (baseDn.CompareTo("") == 0))
				{
					//TODO:  Update to assume root of current domain
					if ((portNumber.Value == (int)LDAP_PORTS.Global_Catalog) || (portNumber.Value == (int)LDAP_PORTS.Secure_Global_Catalog))
					{
						internalBaseDn = string.Empty;
					}
					else
					{
						throw new ApplicationException("baseDn parameter can not be null or blank.");
					}
					
				}
				else
				{
					internalBaseDn = (string)baseDn;
				}


				string internalFilter = filter.IsNull ? string.Empty : (string)filter.Value;
				if (string.IsNullOrEmpty(internalFilter))
				{
					internalFilter = "(&(objectCategory=person)(objectClass=User)(userAccountControl:1.2.840.113556.1.4.803:=512))";
				}
				else if ((!internalFilter.ToLowerInvariant().Contains("(objectcategory=person)")) && (!internalFilter.ToLowerInvariant().Contains("objectclass=user)")))
				{
					internalFilter = "(&(objectCategory=person)(objectClass=User)(userAccountControl:1.2.840.113556.1.4.803:=512)(" + filter.ToString() + "))";
				}

				string internalScopeString = searchScope.IsNull ? string.Empty : (string)searchScope;
				System.DirectoryServices.Protocols.SearchScope internalScope;
				switch (internalScopeString.ToLowerInvariant())
				{
					case "base":
						internalScope = System.DirectoryServices.Protocols.SearchScope.Base;
						break;

					case "onelevel":
						internalScope = System.DirectoryServices.Protocols.SearchScope.OneLevel;
						break;

					case "subtree":
						internalScope = System.DirectoryServices.Protocols.SearchScope.Subtree;
						break;

					case "":
						internalScope = System.DirectoryServices.Protocols.SearchScope.Subtree;
						break;

					default:
						throw new ApplicationException("Invalid search scope.  Allowed values are Base, OneLevel, Subtree, and NULL.");
				}
				#endregion

				try
				{
					WLdapSearchRequest wsr =
						new WLdapSearchRequest((string)connectionPoint
							, (int)portNumber.Value
							, internalBaseDn
							, internalFilter
							, internalScope
							, attrs);
					wsr.LdapSearchClientTimeOut = ConfiguredSettings.LdapClientTimeout;

					return wsr.FindAll();
				}
				catch (LdapException ex)
				{
					if (ex.ErrorCode == 81)
					{
						throw new ApplicationException("Could not contact domain:  " + (string)connectionPoint);
					}
					else
					{
						SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
						throw ex;
					}
				}
				catch (Exception ex)
				{
					SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
					throw ex;
				}

				throw new ApplicationException("Invalid codepath traversed.  Caller should never make it here.");
			}
		}
		finally
		{
			if (Trace.Listeners.Contains(eltl))
			{
				Trace.Listeners.Remove(eltl);
			}
			if (executionContext != null)
			{
				executionContext.Undo();
			}
		}
		return null;
	}

	private static void FillUserObjectRow(
		object o
		, out SqlGuid objectGuid
		, out SqlString distinguishedName
		, out SqlString sAMAccountName
		, out SqlDateTime lastLogonTimestamp
		, out SqlDateTime pwdLastSet
		, out SqlInt32 userAccountControl
		, out SqlBoolean ADS_UF_SCRIPT
		, out SqlBoolean ADS_UF_ACCOUNTDISABLE
		, out SqlBoolean ADS_UF_HOMEDIR_REQUIRED
		, out SqlBoolean ADS_UF_LOCKOUT
		, out SqlBoolean ADS_UF_PASSWD_NOTREQD
		, out SqlBoolean ADS_UF_PASSWD_CANT_CHANGE
		, out SqlBoolean ADS_UF_ENCRYPTED_TEXT_PWD_ALLOWED
		, out SqlBoolean ADS_UF_TEMP_DUPLICATE_ACCOUNT
		, out SqlBoolean ADS_UF_NORMAL_ACCOUNT
		, out SqlBoolean ADS_UF_INTERDOMAIN_TRUST_ACCOUNT
		, out SqlBoolean ADS_UF_WORKSTATION_TRUST_ACCOUNT
		, out SqlBoolean ADS_UF_SERVER_TRUST_ACCOUNT
		, out SqlBoolean ADS_UF_DONT_EXPIRE_PASSWD
		, out SqlBoolean ADS_UF_MNS_LOGON_ACCOUNT
		, out SqlBoolean ADS_UF_SMARTCARD_REQUIRED
		, out SqlBoolean ADS_UF_TRUSTED_FOR_DELEGATION
		, out SqlBoolean ADS_UF_NOT_DELEGATED
		, out SqlBoolean ADS_UF_USE_DES_KEY_ONLY
		, out SqlBoolean ADS_UF_DONT_REQ_PREAUTH
		, out SqlBoolean ADS_UF_PASSWORD_EXPIRED
		, out SqlBoolean ADS_UF_TRUSTED_TO_AUTH_FOR_DELEGATION
		, out SqlBoolean ADS_UF_PARTIAL_SECRETS_ACCOUNT

		//Category specific
		, out SqlString displayName
		, out SqlString msExchHomeServerName
		, out SqlString homeMDB
		, out SqlInt64 mDBOverQuotaLimit
		, out SqlString telephoneNumber
		, out SqlString extensionAttribute7
		, out SqlString employeeId
		, out SqlString msRTCSIP_PrimaryHomeServer
		, out SqlBoolean msRTCSIP_UserEnabled
		, out SqlString msRTCSIP_PrimaryUserAddress
		)
	{
		SearchResultEntry sre = (SearchResultEntry)o;
		if (sre.Attributes.Contains("displayName"))
		{
			displayName = sre.Attributes["displayName"][0].ToString();
		}
		else
		{
			displayName = SqlString.Null;
		}

		if (sre.Attributes.Contains("msExchHomeServerName"))
		{
			msExchHomeServerName = sre.Attributes["msExchHomeServerName"][0].ToString();
		}
		else
		{
			msExchHomeServerName = SqlString.Null;
		}

		if (sre.Attributes.Contains("homeMDB"))
		{
			homeMDB = sre.Attributes["homeMDB"][0].ToString();
		}
		else
		{
			homeMDB = SqlString.Null;
		}

		if (sre.Attributes.Contains("mDBOverQuotaLimit"))
		{
			mDBOverQuotaLimit = Convert.ToInt64(sre.Attributes["mDBOverQuotaLimit"][0]);
		}
		else
		{
			mDBOverQuotaLimit = SqlInt64.Null;
		}

		if (sre.Attributes.Contains("telephoneNumber"))
		{

			telephoneNumber = sre.Attributes["telephoneNumber"][0].ToString();
		}
		else
		{
			telephoneNumber = SqlString.Null;
		}

		if (sre.Attributes.Contains("extensionAttribute7"))
		{
			extensionAttribute7 = sre.Attributes["extensionAttribute7"][0].ToString();
		}
		else
		{
			extensionAttribute7 = SqlString.Null;
		}

		if (sre.Attributes.Contains("employeeId"))
		{
			employeeId = sre.Attributes["employeeId"][0].ToString();
		}
		else
		{
			employeeId = SqlString.Null;
		}

		if (sre.Attributes.Contains("msRTCSIP-PrimaryHomeServer"))
		{
			msRTCSIP_PrimaryHomeServer = sre.Attributes["msRTCSIP-PrimaryHomeServer"][0].ToString();
		}
		else
		{
			msRTCSIP_PrimaryHomeServer = SqlString.Null;
		}

		if (sre.Attributes.Contains("msRTCSIP-UserEnabled"))
		{
			msRTCSIP_UserEnabled = (SqlBoolean)(string.Compare(sre.Attributes["msRTCSIP-UserEnabled"][0].ToString(), "true", true) == 0);
		}
		else
		{
			msRTCSIP_UserEnabled = SqlBoolean.Null;
		}

		if (sre.Attributes.Contains("msRTCSIP-PrimaryUserAddress"))
		{
			msRTCSIP_PrimaryUserAddress = sre.Attributes["msRTCSIP-PrimaryUserAddress"][0].ToString();
		}
		else
		{
			msRTCSIP_PrimaryUserAddress = SqlString.Null;
		}


		sQLSharedLogic.objectClass_User_SharedLogic(
			sre
			, out objectGuid
			, out distinguishedName
			, out sAMAccountName
			, out lastLogonTimestamp
			, out pwdLastSet
			, out userAccountControl
			, out ADS_UF_SCRIPT
			, out ADS_UF_ACCOUNTDISABLE
			, out ADS_UF_HOMEDIR_REQUIRED
			, out ADS_UF_LOCKOUT
			, out ADS_UF_PASSWD_NOTREQD
			, out ADS_UF_PASSWD_CANT_CHANGE
			, out ADS_UF_ENCRYPTED_TEXT_PWD_ALLOWED
			, out ADS_UF_TEMP_DUPLICATE_ACCOUNT
			, out ADS_UF_NORMAL_ACCOUNT
			, out ADS_UF_INTERDOMAIN_TRUST_ACCOUNT
			, out ADS_UF_WORKSTATION_TRUST_ACCOUNT
			, out ADS_UF_SERVER_TRUST_ACCOUNT
			, out ADS_UF_DONT_EXPIRE_PASSWD
			, out ADS_UF_MNS_LOGON_ACCOUNT
			, out ADS_UF_SMARTCARD_REQUIRED
			, out ADS_UF_TRUSTED_FOR_DELEGATION
			, out ADS_UF_NOT_DELEGATED
			, out ADS_UF_USE_DES_KEY_ONLY
			, out ADS_UF_DONT_REQ_PREAUTH
			, out ADS_UF_PASSWORD_EXPIRED
			, out ADS_UF_TRUSTED_TO_AUTH_FOR_DELEGATION
			, out ADS_UF_PARTIAL_SECRETS_ACCOUNT
			);
	}
};
